﻿using System;
using System.Collections.Generic;
using System.Linq;
using Analyze.Manager;
using Analyze.Provider;
using Analyze.Service;
using Common.Model;

namespace Analyze.Calculator
{
    public class BayesClassifier
    {
        private readonly TrainingDataProvider _provider;//训练集管理器
        private static readonly double ZoomFactor = 10.0f;
        /**
        * 默认的构造器，初始化训练集
        */
        public BayesClassifier(TrainingDataProvider provider)
        {
            _provider = provider;
        }
    
        /**
        * 计算给定的文本属性向量X在给定的分类Cj中的类条件概率
        * <code>ClassConditionalProbability</code>连乘值
        * @param X 给定的文本属性向量
        * @param Cj 给定的类别
        * @return 分类条件概率连乘值，即<br>
        */
        double CalcProd(string[] X, string Cj) 
        {
            double ret = 1.0F;
            // 类条件概率连乘
            for (int i = 0; i <X.Length; i++)
            {
                string Xi = X[i];
                //因为结果过小，因此在连乘之前放大10倍，这对最终结果并无影响，因为我们只是比较概率大小而已
                ret *= Probability.CalculateClassConditionalProbability(Xi, Cj) * ZoomFactor;
            }
            // 再乘以先验概率
            ret *= Probability.CalculatePriorProbability(Cj);
            return ret;
        }

        public static double CalcProd(float[] classConditionalProbability,float priorProbability)
        {
            double ret = 1.0F;
            foreach (var p in classConditionalProbability)
            {
                ret *= p * ZoomFactor;
            }
            ret *= priorProbability;
            return ret;
        }
        
        public List<ClassifyResult> GetClassifyResults(List<string> words)
        {
            words = DropStopWords(words);
            
            List<string> classifications = _provider.GetTraningClassifications();//分类
            
            double probility = 0.0F;
            
            List<ClassifyResult> crs =  crs = classifications.Select(ci =>
            {
                int keyWordProbility = 0;
                DateTime sTime = DateTime.Now;
                
                float[] probArray = words.Select(m => _provider.GetWordConditionaProbability(ci, m)).ToArray();
                
                probility = CalcProd(probArray
                    , Probability.CalculatePriorProbability(ci)); //计算给定的文本属性向量在给定的分类Ci中的分类条件概率

                //人工权重部分
                var confCount = probArray.Where(m => m.Equals(1.0f)).Count();

                if (confCount > 0)
                {
                    keyWordProbility = keyWordProbility + confCount;
                }
                //结束人工权重
                
                return new ClassifyResult
                {
                    Classification = ci,
                    Probility = probility,
                    KeyWordProbility = keyWordProbility,
                    ComplexProbility = keyWordProbility+probility
                };
            }).ToList();

            crs = crs.OrderByDescending(m => m.ComplexProbility).ToList();
            
            return crs;
        }
        
//        public string ModelsClassify(List<string> words)
//        {
//            var classifyResults = GetClassifyResults(words);
//            return classifyResults.First(m => m.Probility.Equals(classifyResults.Max(c => c.Probility))).Classification;
//        }
        
        /**
        * 去掉停用词
        * @param text 给定的文本
        * @return 去停用词后结果
        */
        public List<string> DropStopWords(List<string> oldWords)
        {
            return oldWords.Where(w => !StopWordManager.GetInstance().IsStopWord(w)).ToList();
        }
        
        /**
        * 对给定的词语进行分类
        * @param wrods 给定的分词
        * @return 分类结果
        */
        
        public string SourseClassify(string[] words)
        {
            List<string> classifications = _provider.GetTraningClassifications();//分类
            double probility = 0.0F;
            List<ClassifyResult> crs =  crs = classifications.Select(ci =>
            {
                probility = CalcProd(words, ci);//计算给定的文本属性向量terms在给定的分类Ci中的分类条件概率
                return new ClassifyResult
                {
                    Classification = ci,
                    Probility = probility
                };;
            }).ToList();
            double max = crs.Max(m=>m.Probility);
            return crs.First(m => m.Probility.Equals(max)).Classification;
        }
    }
}